/*
* Copyright (c) 2010 Ambiscript.org
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* Except as contained in this notice, the name(s) of the above
* copyright holders shall not be used in advertising or otherwise to
* promote the sale, use or other dealings in this Software without prior
* written authorization.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/


package
{
    import flash.display.*;

	/**
	 * The Alignmant class defines and displays an arrayed set of Sequence classes.
	 */
    public class Alignment extends MovieClip
    {
        private var sequences:Array = new Array();
        private var consensuses:Array = new Array();
        private var homologies:Array = new Array();

        private var sequenceSpacing:int = 190;  // Vertical distance between aligned sequences.
        private var ledgerOffset:int = 600; // Horizontal space between sequence and ledger.
		private var alignmentWindow:Number = 2000.0; // The width of the window which will contain the displayed alignment.

		public var sharedParameters:SharedParameters = new SharedParameters();
		
        public function Alignment(newParameters:SharedParameters) {
			sharedParameters = newParameters;
        }

 
		/**
		 * Resets the alignment by hiding old sequence objects and removing references
		 * to sequences from the array.  This method, which is called whenever the user
		 * changes program settings, allows the modifed sequences to be redrawn on the screen.
		 */
        public function reset():void {
            for (var i:int = 0; i < sequences.length; i ++) {
                sequences[i].hide();
                consensuses[i].hide();
                homologies[i].hide();
            }
            sequences = [];
            consensuses = [];
            homologies = [];
        }


		/**
		 * Adds one or more FASTA-formatted sequences to the alignment.
		 * @param	newFASTA
		 */
        public function addFASTA(newFASTA:String):Boolean {
			newFASTA = newFASTA + "\r"; // Ensures there is a CR at the end of the string to support regex matching.
            var pattern:RegExp = />.+?[\r\n][GATCgatc-]+[\r\n]/sgi;
            var results:Array = new Array();
            results = pattern.exec(newFASTA);
			
			if (results == null) return false;

            while (results != null) {
               var sequence:Sequence = new Sequence(sharedParameters);
               var consensus:Sequence = new Sequence(sharedParameters);
               var homology:Sequence = new Sequence(sharedParameters);

               sequence.addFASTA(results[0]);
               if (sharedParameters.complement) sequence.addSequence(sequence.getComplement());
               sequences.push(sequence);
               addChild(sequences[sequences.length - 1]);

               consensus.addSequence(sequence);
               if (consensuses.length > 0)consensus.addSequence(consensuses[consensuses.length - 1]);
               consensus.sequenceName = "";
               consensuses.push(consensus);
               addChild(consensuses[consensuses.length - 1]);

               
               homology.addSequence(sequences[0]);
               homology.addSequence(sequence);
               homology.sequenceName = sequence.sequenceName;
               homology.hilightPrimarySequence(true);
               homologies.push(homology);
               addChild(homologies[homologies.length - 1]);

               results = pattern.exec(newFASTA);
			   
            }
			
			return true;
        }
		
		/**
		 * Compares two numbers and returns the greater.
		 * @param	a
		 * @param	b
		 */
		public function returnGreaterValue(a:Number, b:Number):Number {
			if (a > b) return a;
			return b;
		}

		/**
		 * Returns the length of the longest sequence in the alignment.
		 * @return
		 */
        public function getMaximumSequenceLength():int {
            var maximumLength:int = 0;
            for (var i:int = 0; i < sequences.length; i ++) {
                if(sequences[i].getLength() > maximumLength) {
                    maximumLength = sequences[i].getLength();
                }
            }
            return maximumLength;
        }

		/**
		 * Plots a new alignment defined by newFASTA.  The String consensusPosition is
         * used to determine whether the consensus sequence(s) will be displayed at the
         * top, bottom, or along the side in leger format.  The String consensusColor
         * is used to specify the color scheme but doesn't work right now.
		 */
        public function plot():void {

            var px:int = 100;
            var py:int = 800;

            reset();
            if (addFASTA(sharedParameters.alignedFASTAsequences))
			{

            if(sharedParameters.consensusFormat == "Top") {   
                consensuses[consensuses.length - 1].draw(px,py);
                py += consensuses[consensuses.length - 1].getHeight();
            }

            for (var i:int = 0; i < sequences.length; i ++) {
                if (sharedParameters.consensusFormat == "Homology") {
                    homologies[i].draw(px, py);
                } else if (sharedParameters.consensusFormat == "Progressive") {
					consensuses[i].sequenceName = sequences[i].sequenceName;
					consensuses[i].draw(px, py);
				} else {
                    sequences[i].draw(px,py);
                }

                if (sharedParameters.consensusFormat == "Ledger") {
                    consensuses[i].draw(px + ledgerOffset + consensuses[consensuses.length - 1].getWidth(), py);
					py += consensuses[i].getHeight();
                } else {
					py += sequences[i].getHeight();
				}
            }

            if(sharedParameters.consensusFormat == "Bottom") {
                consensuses[consensuses.length - 1].draw(px,py);
            }

            // var alignmentScale:Number = sharedParameters.scale * alignmentWindow / alignmentWidth();
			var alignmentScale:Number = sharedParameters.scale * 0.2;
            scaleX = alignmentScale;
            scaleY = alignmentScale;
			}
        }

		/**
		 * Returns the width in pixels of the displayed alignment.
		 * @return
		 */
        public function alignmentWidth():int {
            
            var alignmentWidth:int = 100;
            alignmentWidth = consensuses[consensuses.length - 1].getWidth();
            alignmentWidth += consensuses[consensuses.length - 1].labelWidth;
            if (sharedParameters.consensusFormat == "Ledger") {
                alignmentWidth += consensuses[consensuses.length - 1].getWidth();
                alignmentWidth += ledgerOffset; 
            }
            
            return alignmentWidth;
        }

    }
}